@@ -49,7 +49,7 @@ showEventDescriptions = -> |
||
49 | 49 |
|
50 | 50 |
$(document).ready -> |
51 | 51 |
setupJsonEditor() |
52 |
- $(".multi-select").select2(width: 'resolve') |
|
52 |
+ $(".select2").select2(width: 'resolve') |
|
53 | 53 |
|
54 | 54 |
if $(".top-flash").length |
55 | 55 |
setTimeout((-> $(".top-flash").slideUp(-> $(".top-flash").remove())), 5000) |
@@ -58,8 +58,8 @@ $(document).ready -> |
||
58 | 58 |
|
59 | 59 |
$("#agent_type").on "change", -> |
60 | 60 |
if window.jsonEditor? |
61 |
- $(@).closest(".control-group").find(".spinner").fadeIn(); |
|
62 |
- $("#agent_source_ids ").select2("val", {}); |
|
61 |
+ $(".spinner").fadeIn(); |
|
62 |
+ $("#agent_source_ids").select2("val", {}); |
|
63 | 63 |
$(".event-descriptions").html("").hide() |
64 | 64 |
$.getJSON "/agents/type_details", { type: $(@).val() }, (json) => |
65 | 65 |
if json.can_be_scheduled |
@@ -77,7 +77,7 @@ $(document).ready -> |
||
77 | 77 |
window.jsonEditor.json = json.options |
78 | 78 |
window.jsonEditor.rebuild() |
79 | 79 |
|
80 |
- $(@).closest(".control-group").find(".spinner").stop(true, true).fadeOut(); |
|
80 |
+ $(".spinner").stop(true, true).fadeOut(); |
|
81 | 81 |
|
82 | 82 |
$("#agent_type").change() if $("#agent_type").length |
83 | 83 |
|
@@ -46,7 +46,7 @@ table.events { |
||
46 | 46 |
} |
47 | 47 |
} |
48 | 48 |
|
49 |
-.multi-select { |
|
49 |
+.select2 { |
|
50 | 50 |
float: none !important; |
51 | 51 |
margin-left: 0 !important; |
52 | 52 |
} |
@@ -65,13 +65,8 @@ img.odin { |
||
65 | 65 |
display: none; |
66 | 66 |
} |
67 | 67 |
|
68 |
-.type-select { |
|
69 |
- width: 275px; |
|
70 |
- |
|
71 |
- img.spinner { |
|
72 |
- display: none; |
|
73 |
- float: right; |
|
74 |
- } |
|
68 |
+img.spinner { |
|
69 |
+ display: none; |
|
75 | 70 |
} |
76 | 71 |
|
77 | 72 |
.hidden { |
@@ -14,13 +14,17 @@ module Agents |
||
14 | 14 |
event_description <<-MD |
15 | 15 |
If flights are present then events look like: |
16 | 16 |
|
17 |
- { "cost" : 75.23, |
|
18 |
- "date" : "June 25, 2013", |
|
19 |
- "route" : "New York to Chicago" } |
|
17 |
+ { |
|
18 |
+ "cost": 75.23, |
|
19 |
+ "date": "June 25, 2013", |
|
20 |
+ "route": "New York to Chicago" |
|
21 |
+ } |
|
20 | 22 |
|
21 | 23 |
otherwise |
22 | 24 |
|
23 |
- { "nonetodest" : "No flights found to the specified destination" } |
|
25 |
+ { |
|
26 |
+ "nonetodest": "No flights found to the specified destination" |
|
27 |
+ } |
|
24 | 28 |
MD |
25 | 29 |
|
26 | 30 |
def default_options |
@@ -44,9 +44,7 @@ module Agents |
||
44 | 44 |
} |
45 | 45 |
MD |
46 | 46 |
|
47 |
- event_description <<-MD |
|
48 |
- User defined |
|
49 |
- MD |
|
47 |
+ event_description "User defined" |
|
50 | 48 |
|
51 | 49 |
def validate_options |
52 | 50 |
errors.add(:base, "instructions, mode, skip_agent, and skip_created_at all need to be present.") unless options[:instructions].present? and options[:mode].present? and options[:skip_agent].present? and options[:skip_created_at].present? |
@@ -17,9 +17,14 @@ module Agents |
||
17 | 17 |
MD |
18 | 18 |
|
19 | 19 |
event_description <<-MD |
20 |
- Events look like this: |
|
21 |
- |
|
22 |
- { :message => "Your message", :peak => 6, :peak_time => 3456789242, :grouped_by => "something" } |
|
20 |
+ Events look like: |
|
21 |
+ |
|
22 |
+ { |
|
23 |
+ "message": "Your message", |
|
24 |
+ "peak": 6, |
|
25 |
+ "peak_time": 3456789242, |
|
26 |
+ "grouped_by": "something" |
|
27 |
+ } |
|
23 | 28 |
MD |
24 | 29 |
|
25 | 30 |
def validate_options |
@@ -69,13 +74,13 @@ module Agents |
||
69 | 74 |
if newest_value < second_newest_value && second_newest_value > average_value + std_multiple * standard_deviation |
70 | 75 |
memory[:peaks][group] << second_newest_time |
71 | 76 |
memory[:peaks][group].reject! { |p| p <= second_newest_time - window_duration } |
72 |
- create_event :payload => { :message => options[:message], :peak => second_newest_value, :peak_time => second_newest_time, :grouped_by => group.to_s } |
|
77 |
+ create_event :payload => {:message => options[:message], :peak => second_newest_value, :peak_time => second_newest_time, :grouped_by => group.to_s} |
|
73 | 78 |
end |
74 | 79 |
end |
75 | 80 |
end |
76 | 81 |
|
77 | 82 |
def stats_for(group, options = {}) |
78 |
- data = memory[:data][group].map {|d| d.first.to_f } |
|
83 |
+ data = memory[:data][group].map { |d| d.first.to_f } |
|
79 | 84 |
data = data[0...(memory[:data][group].length - (options[:skip_last] || 0))] |
80 | 85 |
length = data.length.to_f |
81 | 86 |
mean = 0 |
@@ -1,43 +1,41 @@ |
||
1 | 1 |
module Agents |
2 |
- class PostAgent < Agent |
|
3 |
- cannot_be_scheduled! |
|
2 |
+ class PostAgent < Agent |
|
3 |
+ cannot_be_scheduled! |
|
4 | 4 |
|
5 |
- description <<-MD |
|
6 |
- Post Agent receives events from other agents and send those events as the contents of a post request to a specified url. `post_url` field must specify where you would like to receive post requests and do not forget to include URI scheme(`http` or `https`) |
|
7 |
- MD |
|
5 |
+ description <<-MD |
|
6 |
+ Post Agent receives events from other agents and send those events as the contents of a post request to a specified url. `post_url` field must specify where you would like to receive post requests and do not forget to include URI scheme (`http` or `https`) |
|
7 |
+ MD |
|
8 | 8 |
|
9 |
- event_description <<-MD |
|
10 |
- Does not produce any event. |
|
11 |
- MD |
|
9 |
+ event_description "Does not produce events." |
|
12 | 10 |
|
13 |
- def default_options |
|
14 |
- { |
|
15 |
- :post_url => "http://www.example.com", |
|
16 |
- :expected_receive_period_in_days => 1 |
|
17 |
- } |
|
18 |
- end |
|
11 |
+ def default_options |
|
12 |
+ { |
|
13 |
+ :post_url => "http://www.example.com", |
|
14 |
+ :expected_receive_period_in_days => 1 |
|
15 |
+ } |
|
16 |
+ end |
|
19 | 17 |
|
20 |
- def working? |
|
21 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago |
|
22 |
- end |
|
18 |
+ def working? |
|
19 |
+ last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago |
|
20 |
+ end |
|
23 | 21 |
|
24 |
- def validate_options |
|
25 |
- unless options[:post_url].present? && options[:expected_receive_period_in_days].present? |
|
26 |
- errors.add(:base, "post_url and expected_receive_period_in_days are required fields") |
|
27 |
- end |
|
28 |
- end |
|
22 |
+ def validate_options |
|
23 |
+ unless options[:post_url].present? && options[:expected_receive_period_in_days].present? |
|
24 |
+ errors.add(:base, "post_url and expected_receive_period_in_days are required fields") |
|
25 |
+ end |
|
26 |
+ end |
|
29 | 27 |
|
30 |
- def post_event(uri,event) |
|
31 |
- req = Net::HTTP::Post.new(uri.request_uri) |
|
32 |
- req.form_data = event |
|
33 |
- Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) } |
|
34 |
- end |
|
28 |
+ def post_event(uri, event) |
|
29 |
+ req = Net::HTTP::Post.new(uri.request_uri) |
|
30 |
+ req.form_data = event |
|
31 |
+ Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) } |
|
32 |
+ end |
|
35 | 33 |
|
36 |
- def receive(incoming_events) |
|
37 |
- incoming_events.each do |event| |
|
38 |
- uri = URI options[:post_url] |
|
39 |
- post_event uri, event.payload |
|
40 |
- end |
|
41 |
- end |
|
34 |
+ def receive(incoming_events) |
|
35 |
+ incoming_events.each do |event| |
|
36 |
+ uri = URI options[:post_url] |
|
37 |
+ post_event uri, event.payload |
|
38 |
+ end |
|
42 | 39 |
end |
40 |
+ end |
|
43 | 41 |
end |
@@ -1,84 +1,85 @@ |
||
1 | 1 |
require 'csv' |
2 | 2 |
|
3 | 3 |
module Agents |
4 |
- class SentimentAgent < Agent |
|
5 |
- class_attribute :anew |
|
4 |
+ class SentimentAgent < Agent |
|
5 |
+ class_attribute :anew |
|
6 | 6 |
|
7 |
- cannot_be_scheduled! |
|
7 |
+ cannot_be_scheduled! |
|
8 | 8 |
|
9 |
- description <<-MD |
|
10 |
- The SentimentAgent generates `good-bad` (psychological valence or happiness index), `active-passive` (arousal), |
|
11 |
- and `strong-weak` (dominance) score. It will output a value between 1 and 9. It will only work on English content. |
|
9 |
+ description <<-MD |
|
10 |
+ The SentimentAgent generates `good-bad` (psychological valence or happiness index), `active-passive` (arousal), |
|
11 |
+ and `strong-weak` (dominance) score. It will output a value between 1 and 9. It will only work on English content. |
|
12 | 12 |
|
13 |
- Make sure the content this agent is analyzing have sufficient length to get respectable results. |
|
13 |
+ Make sure the content this agent is analyzing have sufficient length to get respectable results. |
|
14 | 14 |
|
15 |
- Provide a JSONPath in `content` field where content is residing and set `expected_receive_period_in_days` to the maximum number of days you would allow to be passed between events being received by this agent. |
|
16 |
- MD |
|
15 |
+ Provide a JSONPath in `content` field where content is residing and set `expected_receive_period_in_days` to the maximum number of days you would allow to be passed between events being received by this agent. |
|
16 |
+ MD |
|
17 | 17 |
|
18 |
- event_description <<-MD |
|
19 |
- Events look like: |
|
20 |
- { |
|
21 |
- :content => "The quick brown fox jumps over the lazy dog.", |
|
22 |
- :valence => 6.196666666666666, |
|
23 |
- :arousal => 4.993333333333333, |
|
24 |
- :dominance => 5.63 |
|
25 |
- } |
|
26 |
- MD |
|
18 |
+ event_description <<-MD |
|
19 |
+ Events look like: |
|
27 | 20 |
|
28 |
- def default_options |
|
29 |
- { |
|
30 |
- :content => "$.message.text[*]", |
|
31 |
- :expected_receive_period_in_days => 1 |
|
32 |
- } |
|
33 |
- end |
|
21 |
+ { |
|
22 |
+ "content": "The quick brown fox jumps over the lazy dog.", |
|
23 |
+ "valence": 6.196666666666666, |
|
24 |
+ "arousal": 4.993333333333333, |
|
25 |
+ "dominance": 5.63 |
|
26 |
+ } |
|
27 |
+ MD |
|
34 | 28 |
|
35 |
- def working? |
|
36 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago |
|
37 |
- end |
|
29 |
+ def default_options |
|
30 |
+ { |
|
31 |
+ :content => "$.message.text[*]", |
|
32 |
+ :expected_receive_period_in_days => 1 |
|
33 |
+ } |
|
34 |
+ end |
|
38 | 35 |
|
39 |
- def receive(incoming_events) |
|
40 |
- anew = self.class.sentiment_hash |
|
41 |
- incoming_events.each do |event| |
|
42 |
- Utils.values_at(event.payload, options[:content]).each do |content| |
|
43 |
- sent_values = sentiment_values anew, content |
|
44 |
- create_event :payload => {:content => content, |
|
45 |
- :valence => sent_values[0], |
|
46 |
- :arousal => sent_values[1], |
|
47 |
- :dominance => sent_values[2], |
|
48 |
- :original_event => event.payload} |
|
49 |
- end |
|
50 |
- end |
|
51 |
- end |
|
36 |
+ def working? |
|
37 |
+ last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago |
|
38 |
+ end |
|
52 | 39 |
|
53 |
- def validate_options |
|
54 |
- errors.add(:base, "content and expected_receive_period_in_days must be present") unless options[:content].present? && options[:expected_receive_period_in_days].present? |
|
40 |
+ def receive(incoming_events) |
|
41 |
+ anew = self.class.sentiment_hash |
|
42 |
+ incoming_events.each do |event| |
|
43 |
+ Utils.values_at(event.payload, options[:content]).each do |content| |
|
44 |
+ sent_values = sentiment_values anew, content |
|
45 |
+ create_event :payload => { :content => content, |
|
46 |
+ :valence => sent_values[0], |
|
47 |
+ :arousal => sent_values[1], |
|
48 |
+ :dominance => sent_values[2], |
|
49 |
+ :original_event => event.payload } |
|
55 | 50 |
end |
51 |
+ end |
|
52 |
+ end |
|
53 |
+ |
|
54 |
+ def validate_options |
|
55 |
+ errors.add(:base, "content and expected_receive_period_in_days must be present") unless options[:content].present? && options[:expected_receive_period_in_days].present? |
|
56 |
+ end |
|
56 | 57 |
|
57 |
- def self.sentiment_hash |
|
58 |
- unless self.anew |
|
59 |
- self.anew = {} |
|
60 |
- CSV.foreach Rails.root.join('data/anew.csv') do |row| |
|
61 |
- self.anew[row[0]] = row.values_at(2,4,6).map {|val| val.to_f} |
|
62 |
- end |
|
63 |
- end |
|
64 |
- self.anew |
|
58 |
+ def self.sentiment_hash |
|
59 |
+ unless self.anew |
|
60 |
+ self.anew = {} |
|
61 |
+ CSV.foreach Rails.root.join('data/anew.csv') do |row| |
|
62 |
+ self.anew[row[0]] = row.values_at(2, 4, 6).map { |val| val.to_f } |
|
65 | 63 |
end |
64 |
+ end |
|
65 |
+ self.anew |
|
66 |
+ end |
|
66 | 67 |
|
67 |
- def sentiment_values(anew,text) |
|
68 |
- valence, arousal, dominance, freq = [0] * 4 |
|
69 |
- text.downcase.strip.gsub(/[^a-z ]/,"").split.each do |word| |
|
70 |
- if anew.has_key? word |
|
71 |
- valence += anew[word][0] |
|
72 |
- arousal += anew[word][1] |
|
73 |
- dominance += anew[word][2] |
|
74 |
- freq += 1 |
|
75 |
- end |
|
76 |
- end |
|
77 |
- if valence != 0 |
|
78 |
- [valence/freq, arousal/freq, dominance/freq] |
|
79 |
- else |
|
80 |
- ["Insufficient data for meaningful answer"] * 3 |
|
81 |
- end |
|
68 |
+ def sentiment_values(anew, text) |
|
69 |
+ valence, arousal, dominance, freq = [0] * 4 |
|
70 |
+ text.downcase.strip.gsub(/[^a-z ]/, "").split.each do |word| |
|
71 |
+ if anew.has_key? word |
|
72 |
+ valence += anew[word][0] |
|
73 |
+ arousal += anew[word][1] |
|
74 |
+ dominance += anew[word][2] |
|
75 |
+ freq += 1 |
|
82 | 76 |
end |
77 |
+ end |
|
78 |
+ if valence != 0 |
|
79 |
+ [valence/freq, arousal/freq, dominance/freq] |
|
80 |
+ else |
|
81 |
+ ["Insufficient data for meaningful answer"] * 3 |
|
82 |
+ end |
|
83 | 83 |
end |
84 |
+ end |
|
84 | 85 |
end |
@@ -1,80 +1,78 @@ |
||
1 | 1 |
module Agents |
2 |
- class TranslationAgent < Agent |
|
2 |
+ class TranslationAgent < Agent |
|
3 | 3 |
|
4 |
- cannot_be_scheduled! |
|
4 |
+ cannot_be_scheduled! |
|
5 | 5 |
|
6 |
- description <<-MD |
|
7 |
- You can use Translation Agent to translate text between natural languages. |
|
8 |
- Services are provided using Microsoft Translator. You can [sign up](https://datamarket.azure.com/dataset/bing/microsofttranslator) and [register your application](https://datamarket.azure.com/developer/applications/register) to get `client_id` and `client_secret` which are required to use this agent. |
|
9 |
- `to` must be filled with a [translator language code](http://msdn.microsoft.com/en-us/library/hh456380.aspx). |
|
6 |
+ description <<-MD |
|
7 |
+ You can use Translation Agent to translate text between natural languages. |
|
8 |
+ Services are provided using Microsoft Translator. You can [sign up](https://datamarket.azure.com/dataset/bing/microsofttranslator) and [register your application](https://datamarket.azure.com/developer/applications/register) to get `client_id` and `client_secret` which are required to use this agent. |
|
9 |
+ `to` must be filled with a [translator language code](http://msdn.microsoft.com/en-us/library/hh456380.aspx). |
|
10 | 10 |
|
11 |
- Specify what you would like to translate in `content` field, by specifying key and JSONPath of content to be translated. |
|
11 |
+ Specify what you would like to translate in `content` field, by specifying key and JSONPath of content to be translated. |
|
12 | 12 |
|
13 |
- `expected_receive_period_in_days` is the maximum number of days you would allow to pass between events. |
|
14 |
- MD |
|
13 |
+ `expected_receive_period_in_days` is the maximum number of days you would allow to pass between events. |
|
14 |
+ MD |
|
15 | 15 |
|
16 |
- event_description <<-MD |
|
17 |
- User defined |
|
18 |
- MD |
|
16 |
+ event_description "User defined" |
|
19 | 17 |
|
20 |
- def default_options |
|
21 |
- { |
|
22 |
- :client_id => "xxxxxx", |
|
23 |
- :client_secret => "xxxxxx" , |
|
24 |
- :to => "fi", |
|
25 |
- :expected_receive_period_in_days => 1, |
|
26 |
- :content => { |
|
27 |
- :text => "$.message.text", |
|
28 |
- :content => "$.xyz" |
|
29 |
- } |
|
30 |
- } |
|
31 |
- end |
|
18 |
+ def default_options |
|
19 |
+ { |
|
20 |
+ :client_id => "xxxxxx", |
|
21 |
+ :client_secret => "xxxxxx", |
|
22 |
+ :to => "fi", |
|
23 |
+ :expected_receive_period_in_days => 1, |
|
24 |
+ :content => { |
|
25 |
+ :text => "$.message.text", |
|
26 |
+ :content => "$.xyz" |
|
27 |
+ } |
|
28 |
+ } |
|
29 |
+ end |
|
32 | 30 |
|
33 |
- def working? |
|
34 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago |
|
35 |
- end |
|
31 |
+ def working? |
|
32 |
+ last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago |
|
33 |
+ end |
|
36 | 34 |
|
37 |
- def translate(text,to,access_token) |
|
38 |
- translate_uri = URI 'http://api.microsofttranslator.com/v2/Ajax.svc/Translate' |
|
39 |
- params = { |
|
40 |
- :text => text, |
|
41 |
- :to => to |
|
42 |
- } |
|
43 |
- translate_uri.query = URI.encode_www_form params |
|
44 |
- request = Net::HTTP::Get.new translate_uri.request_uri |
|
45 |
- request['Authorization'] = "Bearer" + " " + access_token |
|
46 |
- http = Net::HTTP.new translate_uri.hostname, translate_uri.port |
|
47 |
- response = http.request request |
|
48 |
- YAML.load response.body |
|
49 |
- end |
|
35 |
+ def translate(text, to, access_token) |
|
36 |
+ translate_uri = URI 'http://api.microsofttranslator.com/v2/Ajax.svc/Translate' |
|
37 |
+ params = { |
|
38 |
+ :text => text, |
|
39 |
+ :to => to |
|
40 |
+ } |
|
41 |
+ translate_uri.query = URI.encode_www_form params |
|
42 |
+ request = Net::HTTP::Get.new translate_uri.request_uri |
|
43 |
+ request['Authorization'] = "Bearer" + " " + access_token |
|
44 |
+ http = Net::HTTP.new translate_uri.hostname, translate_uri.port |
|
45 |
+ response = http.request request |
|
46 |
+ YAML.load response.body |
|
47 |
+ end |
|
50 | 48 |
|
51 |
- def validate_options |
|
52 |
- unless options[:client_id].present? && options[:client_secret].present? && options[:to].present? && options[:content].present? && options[:expected_receive_period_in_days].present? |
|
53 |
- errors.add :base, "client_id,client_secret,to,expected_receive_period_in_days and content are all required" |
|
54 |
- end |
|
55 |
- end |
|
49 |
+ def validate_options |
|
50 |
+ unless options[:client_id].present? && options[:client_secret].present? && options[:to].present? && options[:content].present? && options[:expected_receive_period_in_days].present? |
|
51 |
+ errors.add :base, "client_id,client_secret,to,expected_receive_period_in_days and content are all required" |
|
52 |
+ end |
|
53 |
+ end |
|
56 | 54 |
|
57 |
- def postform(uri,params) |
|
58 |
- req = Net::HTTP::Post.new(uri.request_uri) |
|
59 |
- req.form_data = params |
|
60 |
- Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) { |http| http.request(req) } |
|
61 |
- end |
|
55 |
+ def postform(uri, params) |
|
56 |
+ req = Net::HTTP::Post.new(uri.request_uri) |
|
57 |
+ req.form_data = params |
|
58 |
+ Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) { |http| http.request(req) } |
|
59 |
+ end |
|
62 | 60 |
|
63 |
- def receive(incoming_events) |
|
64 |
- auth_uri = URI "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13" |
|
65 |
- response = postform auth_uri, :client_id => options[:client_id], |
|
66 |
- :client_secret => options[:client_secret], |
|
67 |
- :scope => "http://api.microsofttranslator.com", |
|
68 |
- :grant_type =>"client_credentials" |
|
69 |
- access_token = JSON.parse(response.body)["access_token"] |
|
70 |
- incoming_events.each do |event| |
|
71 |
- translated_event = {} |
|
72 |
- options[:content].each_pair do |key,value| |
|
73 |
- to_be_translated = Utils.values_at event.payload, value |
|
74 |
- translated_event[key] = translate to_be_translated.first, options[:to], access_token |
|
75 |
- end |
|
76 |
- create_event :payload => translated_event |
|
77 |
- end |
|
61 |
+ def receive(incoming_events) |
|
62 |
+ auth_uri = URI "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13" |
|
63 |
+ response = postform auth_uri, :client_id => options[:client_id], |
|
64 |
+ :client_secret => options[:client_secret], |
|
65 |
+ :scope => "http://api.microsofttranslator.com", |
|
66 |
+ :grant_type => "client_credentials" |
|
67 |
+ access_token = JSON.parse(response.body)["access_token"] |
|
68 |
+ incoming_events.each do |event| |
|
69 |
+ translated_event = {} |
|
70 |
+ options[:content].each_pair do |key, value| |
|
71 |
+ to_be_translated = Utils.values_at event.payload, value |
|
72 |
+ translated_event[key] = translate to_be_translated.first, options[:to], access_token |
|
78 | 73 |
end |
74 |
+ create_event :payload => translated_event |
|
75 |
+ end |
|
79 | 76 |
end |
77 |
+ end |
|
80 | 78 |
end |
@@ -19,7 +19,7 @@ module Agents |
||
19 | 19 |
event_description <<-MD |
20 | 20 |
Events look like this: |
21 | 21 |
|
22 |
- { :message => "Your message" } |
|
22 |
+ { "message": "Your message" } |
|
23 | 23 |
MD |
24 | 24 |
|
25 | 25 |
def validate_options |
@@ -19,30 +19,33 @@ module Agents |
||
19 | 19 |
When in `counts` mode, TwitterStreamAgent events look like: |
20 | 20 |
|
21 | 21 |
{ |
22 |
- :filter => "hello world", |
|
23 |
- :count => 25, |
|
24 |
- :time => 3456785456 |
|
22 |
+ "filter": "hello world", |
|
23 |
+ "count": 25, |
|
24 |
+ "time": 3456785456 |
|
25 | 25 |
} |
26 | 26 |
|
27 | 27 |
When in `events` mode, TwitterStreamAgent events look like: |
28 | 28 |
|
29 |
- { :filter=>"selectorgadget", |
|
29 |
+ { |
|
30 |
+ "filter": "selectorgadget", |
|
30 | 31 |
... every Tweet field, including ... |
31 |
- :text=> "something", |
|
32 |
- :user=> |
|
33 |
- { :name=>"Mr. Someone", |
|
34 |
- :screen_name=>"Someone", |
|
35 |
- :location=>"Vancouver BC Canada", |
|
36 |
- :description=> "...", |
|
37 |
- :followers_count=>486, |
|
38 |
- :friends_count=>1983, |
|
39 |
- :created_at=>"Mon Aug 29 23:38:14 +0000 2011", |
|
40 |
- :time_zone=>"Pacific Time (US & Canada)", |
|
41 |
- :statuses_count=>3807, |
|
42 |
- :lang=>"en" }, |
|
43 |
- :retweet_count=>0, |
|
44 |
- :entities=> ... |
|
45 |
- :lang=>"en" } |
|
32 |
+ "text": "something", |
|
33 |
+ "user": { |
|
34 |
+ "name": "Mr. Someone", |
|
35 |
+ "screen_name": "Someone", |
|
36 |
+ "location": "Vancouver BC Canada", |
|
37 |
+ "description": "...", |
|
38 |
+ "followers_count": 486, |
|
39 |
+ "friends_count": 1983, |
|
40 |
+ "created_at": "Mon Aug 29 23:38:14 +0000 2011", |
|
41 |
+ "time_zone": "Pacific Time (US & Canada)", |
|
42 |
+ "statuses_count": 3807, |
|
43 |
+ "lang": "en" |
|
44 |
+ }, |
|
45 |
+ "retweet_count": 0, |
|
46 |
+ "entities": ... |
|
47 |
+ "lang": "en" |
|
48 |
+ } |
|
46 | 49 |
MD |
47 | 50 |
|
48 | 51 |
default_schedule "11pm" |
@@ -15,95 +15,25 @@ module Agents |
||
15 | 15 |
event_description <<-MD |
16 | 16 |
Events are the raw JSON provided by the Twitter API. Should look something like: |
17 | 17 |
|
18 |
- { |
|
19 |
- :created_at=>"Thu Apr 04 13:27:48 +0000 2013", |
|
20 |
- :id=>319803490421596160, |
|
21 |
- :id_str=>"319803490421596160", |
|
22 |
- :text=> |
|
23 |
- "In which @jeresig goes to an art gallery and is \"the JavaScript programmer\". http://t.co/gt3PT1d3G1", |
|
24 |
- :source=> |
|
25 |
- "<a href=\"http://itunes.apple.com/us/app/twitter/id409789998?mt=12\" rel=\"nofollow\">Twitter for Mac</a>", |
|
26 |
- :truncated=>false, |
|
27 |
- :in_reply_to_status_id=>nil, |
|
28 |
- :in_reply_to_status_id_str=>nil, |
|
29 |
- :in_reply_to_user_id=>nil, |
|
30 |
- :in_reply_to_user_id_str=>nil, |
|
31 |
- :in_reply_to_screen_name=>nil, |
|
32 |
- :user=> |
|
33 |
- {:id=>2341001, |
|
34 |
- :id_str=>"2341001", |
|
35 |
- :name=>"Albert Sun", |
|
36 |
- :screen_name=>"albertsun", |
|
37 |
- :location=>"New York, NY", |
|
38 |
- :description=> |
|
39 |
- "News apps developer at NYT, formerly WSJ. graduated Penn 2010, geek, journalist, data-viz, nlp, gis, digital economics =)", |
|
40 |
- :url=>"http://albertsun.info", |
|
41 |
- :entities=> |
|
42 |
- {:url=> |
|
43 |
- {:urls=> |
|
44 |
- [{:url=>"http://albertsun.info", |
|
45 |
- :expanded_url=>nil, |
|
46 |
- :indices=>[0, 21]}]}, |
|
47 |
- :description=>{:urls=>[]}}, |
|
48 |
- :protected=>false, |
|
49 |
- :followers_count=>1857, |
|
50 |
- :friends_count=>798, |
|
51 |
- :listed_count=>115, |
|
52 |
- :created_at=>"Mon Mar 26 19:22:05 +0000 2007", |
|
53 |
- :favourites_count=>9, |
|
54 |
- :utc_offset=>-18000, |
|
55 |
- :time_zone=>"Eastern Time (US & Canada)", |
|
56 |
- :geo_enabled=>false, |
|
57 |
- :verified=>false, |
|
58 |
- :statuses_count=>2572, |
|
59 |
- :lang=>"en", |
|
60 |
- :contributors_enabled=>false, |
|
61 |
- :is_translator=>false, |
|
62 |
- :profile_background_color=>"1B2A2B", |
|
63 |
- :profile_background_image_url=> |
|
64 |
- "http://a0.twimg.com/profile_background_images/2802438/twitterbg.jpg", |
|
65 |
- :profile_background_image_url_https=> |
|
66 |
- "https://si0.twimg.com/profile_background_images/2802438/twitterbg.jpg", |
|
67 |
- :profile_background_tile=>false, |
|
68 |
- :profile_image_url=> |
|
69 |
- "http://a0.twimg.com/profile_images/110500205/profile-square_normal.jpg", |
|
70 |
- :profile_image_url_https=> |
|
71 |
- "https://si0.twimg.com/profile_images/110500205/profile-square_normal.jpg", |
|
72 |
- :profile_link_color=>"0000FF", |
|
73 |
- :profile_sidebar_border_color=>"87BC44", |
|
74 |
- :profile_sidebar_fill_color=>"E0FF92", |
|
75 |
- :profile_text_color=>"000000", |
|
76 |
- :profile_use_background_image=>true, |
|
77 |
- :default_profile=>false, |
|
78 |
- :default_profile_image=>false, |
|
79 |
- :following=>false, |
|
80 |
- :follow_request_sent=>false, |
|
81 |
- :notifications=>false}, |
|
82 |
- :geo=>nil, |
|
83 |
- :coordinates=>nil, |
|
84 |
- :place=>nil, |
|
85 |
- :contributors=>nil, |
|
86 |
- :retweet_count=>0, |
|
87 |
- :favorite_count=>0, |
|
88 |
- :entities=> |
|
89 |
- {:hashtags=>[], |
|
90 |
- :urls=> |
|
91 |
- [{:url=>"http://t.co/gt3PT1d3G1", |
|
92 |
- :expanded_url=> |
|
93 |
- "http://www.nytimes.com/2013/04/04/fashion/art-and-techology-a-clash-of-cultures.html?pagewanted=all", |
|
94 |
- :display_url=>"nytimes.com/2013/04/04/fas", |
|
95 |
- :indices=>[77, 99]}], |
|
96 |
- :user_mentions=> |
|
97 |
- [{:screen_name=>"jeresig", |
|
98 |
- :name=>"John Resig", |
|
99 |
- :id=>752673, |
|
100 |
- :id_str=>"752673", |
|
101 |
- :indices=>[9, 17]}]}, |
|
102 |
- :favorited=>false, |
|
103 |
- :retweeted=>false, |
|
104 |
- :possibly_sensitive=>false, |
|
105 |
- :lang=>"en" |
|
106 |
- } |
|
18 |
+ { |
|
19 |
+ ... every Tweet field, including ... |
|
20 |
+ "text": "something", |
|
21 |
+ "user": { |
|
22 |
+ "name": "Mr. Someone", |
|
23 |
+ "screen_name": "Someone", |
|
24 |
+ "location": "Vancouver BC Canada", |
|
25 |
+ "description": "...", |
|
26 |
+ "followers_count": 486, |
|
27 |
+ "friends_count": 1983, |
|
28 |
+ "created_at": "Mon Aug 29 23:38:14 +0000 2011", |
|
29 |
+ "time_zone": "Pacific Time (US & Canada)", |
|
30 |
+ "statuses_count": 3807, |
|
31 |
+ "lang": "en" |
|
32 |
+ }, |
|
33 |
+ "retweet_count": 0, |
|
34 |
+ "entities": ... |
|
35 |
+ "lang": "en" |
|
36 |
+ } |
|
107 | 37 |
MD |
108 | 38 |
|
109 | 39 |
default_schedule "every_1h" |
@@ -17,15 +17,15 @@ module Agents |
||
17 | 17 |
Assuming you're using the iOS application, events look like this: |
18 | 18 |
|
19 | 19 |
{ |
20 |
- :latitude => "37.12345", |
|
21 |
- :longitude => "-122.12345", |
|
22 |
- :timestamp => "123456789.0", |
|
23 |
- :altitude => "22.0", |
|
24 |
- :horizontal_accuracy => "5.0", |
|
25 |
- :vertical_accuracy => "3.0", |
|
26 |
- :speed => "0.52595", |
|
27 |
- :course => "72.0703", |
|
28 |
- :device_token => "..." |
|
20 |
+ "latitude": "37.12345", |
|
21 |
+ "longitude": "-122.12345", |
|
22 |
+ "timestamp": "123456789.0", |
|
23 |
+ "altitude": "22.0", |
|
24 |
+ "horizontal_accuracy": "5.0", |
|
25 |
+ "vertical_accuracy": "3.0", |
|
26 |
+ "speed": "0.52595", |
|
27 |
+ "course": "72.0703", |
|
28 |
+ "device_token": "..." |
|
29 | 29 |
} |
30 | 30 |
MD |
31 | 31 |
|
@@ -14,26 +14,24 @@ module Agents |
||
14 | 14 |
Events look like this: |
15 | 15 |
|
16 | 16 |
{ |
17 |
- :zipcode => 12345, |
|
18 |
- :date => { :epoch=>"1357959600", :pretty=>"10:00 PM EST on January 11, 2013" }, |
|
19 |
- :high => { :fahrenheit=>"64", :celsius=>"18" }, |
|
20 |
- :low => { :fahrenheit=>"52", :celsius=>"11" }, |
|
21 |
- :conditions => "Rain Showers", |
|
22 |
- :icon=>"rain", |
|
23 |
- :icon_url => "http://icons-ak.wxug.com/i/c/k/rain.gif", |
|
24 |
- :skyicon => "mostlycloudy", |
|
25 |
- :pop => 80, |
|
26 |
- :qpf_allday => { :in=>0.24, :mm=>6.1 }, |
|
27 |
- :qpf_day => { :in=>0.13, :mm=>3.3 }, |
|
28 |
- :qpf_night => { :in=>0.03, :mm=>0.8 }, |
|
29 |
- :snow_allday => { :in=>0, :cm=>0 }, |
|
30 |
- :snow_day => { :in=>0, :cm=>0 }, |
|
31 |
- :snow_night => { :in=>0, :cm=>0 }, |
|
32 |
- :maxwind => { :mph=>15, :kph=>24, :dir=>"SSE", :degrees=>160 }, |
|
33 |
- :avewind => { :mph=>9, :kph=>14, :dir=>"SSW", :degrees=>194 }, |
|
34 |
- :avehumidity => 85, |
|
35 |
- :maxhumidity => 93, |
|
36 |
- :minhumidity => 63 |
|
17 |
+ "zipcode": 12345, |
|
18 |
+ "date": { |
|
19 |
+ "epoch": "1357959600", |
|
20 |
+ "pretty": "10:00 PM EST on January 11, 2013" |
|
21 |
+ }, |
|
22 |
+ "high": { |
|
23 |
+ "fahrenheit": "64", |
|
24 |
+ "celsius": "18" |
|
25 |
+ }, |
|
26 |
+ "low": { |
|
27 |
+ "fahrenheit": "52", |
|
28 |
+ "celsius": "11" |
|
29 |
+ }, |
|
30 |
+ "conditions": "Rain Showers", |
|
31 |
+ "icon": "rain", |
|
32 |
+ "icon_url": "http://icons-ak.wxug.com/i/c/k/rain.gif", |
|
33 |
+ "skyicon": "mostlycloudy", |
|
34 |
+ ... |
|
37 | 35 |
} |
38 | 36 |
MD |
39 | 37 |
|
@@ -36,11 +36,7 @@ module Agents |
||
36 | 36 |
MD |
37 | 37 |
|
38 | 38 |
event_description do |
39 |
- <<-MD |
|
40 |
- Events will have the fields you specified. Your options look like: |
|
41 |
- |
|
42 |
- #{PP.pp(options[:extract], "")} |
|
43 |
- MD |
|
39 |
+ "Events will have the fields you specified. Your options look like:\n\n #{Utils.pretty_print options[:extract]}" |
|
44 | 40 |
end |
45 | 41 |
|
46 | 42 |
default_schedule "every_12h" |
@@ -16,51 +16,51 @@ module Agents |
||
16 | 16 |
MD |
17 | 17 |
|
18 | 18 |
event_description <<-MD |
19 |
- Events are the raw JSON provided by the Twitter API. Should look something like: |
|
20 |
- |
|
21 |
- { |
|
22 |
- "created_at": "Tue May 31 17:46:55 +0800 2011", |
|
23 |
- "id": 11488058246, |
|
24 |
- "text": "求关注。", |
|
25 |
- "source": "<a href=\"http://weibo.com\" rel=\"nofollow\">新浪微博</a>", |
|
26 |
- "favorited": false, |
|
27 |
- "truncated": false, |
|
28 |
- "in_reply_to_status_id": "", |
|
29 |
- "in_reply_to_user_id": "", |
|
30 |
- "in_reply_to_screen_name": "", |
|
31 |
- "geo": null, |
|
32 |
- "mid": "5612814510546515491", |
|
33 |
- "reposts_count": 8, |
|
34 |
- "comments_count": 9, |
|
35 |
- "annotations": [], |
|
36 |
- "user": { |
|
37 |
- "id": 1404376560, |
|
38 |
- "screen_name": "zaku", |
|
39 |
- "name": "zaku", |
|
40 |
- "province": "11", |
|
41 |
- "city": "5", |
|
42 |
- "location": "北京 朝阳区", |
|
43 |
- "description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。", |
|
44 |
- "url": "http://blog.sina.com.cn/zaku", |
|
45 |
- "profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1", |
|
46 |
- "domain": "zaku", |
|
47 |
- "gender": "m", |
|
48 |
- "followers_count": 1204, |
|
49 |
- "friends_count": 447, |
|
50 |
- "statuses_count": 2908, |
|
51 |
- "favourites_count": 0, |
|
52 |
- "created_at": "Fri Aug 28 00:00:00 +0800 2009", |
|
53 |
- "following": false, |
|
54 |
- "allow_all_act_msg": false, |
|
55 |
- "remark": "", |
|
56 |
- "geo_enabled": true, |
|
57 |
- "verified": false, |
|
58 |
- "allow_all_comment": true, |
|
59 |
- "avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1", |
|
60 |
- "verified_reason": "", |
|
61 |
- "follow_me": false, |
|
62 |
- "online_status": 0, |
|
63 |
- "bi_followers_count": 215 |
|
19 |
+ Events are the raw JSON provided by the Weibo API. Should look something like: |
|
20 |
+ |
|
21 |
+ { |
|
22 |
+ "created_at": "Tue May 31 17:46:55 +0800 2011", |
|
23 |
+ "id": 11488058246, |
|
24 |
+ "text": "求关注。", |
|
25 |
+ "source": "<a href=\"http://weibo.com\" rel=\"nofollow\">新浪微博</a>", |
|
26 |
+ "favorited": false, |
|
27 |
+ "truncated": false, |
|
28 |
+ "in_reply_to_status_id": "", |
|
29 |
+ "in_reply_to_user_id": "", |
|
30 |
+ "in_reply_to_screen_name": "", |
|
31 |
+ "geo": null, |
|
32 |
+ "mid": "5612814510546515491", |
|
33 |
+ "reposts_count": 8, |
|
34 |
+ "comments_count": 9, |
|
35 |
+ "annotations": [], |
|
36 |
+ "user": { |
|
37 |
+ "id": 1404376560, |
|
38 |
+ "screen_name": "zaku", |
|
39 |
+ "name": "zaku", |
|
40 |
+ "province": "11", |
|
41 |
+ "city": "5", |
|
42 |
+ "location": "北京 朝阳区", |
|
43 |
+ "description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。", |
|
44 |
+ "url": "http://blog.sina.com.cn/zaku", |
|
45 |
+ "profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1", |
|
46 |
+ "domain": "zaku", |
|
47 |
+ "gender": "m", |
|
48 |
+ "followers_count": 1204, |
|
49 |
+ "friends_count": 447, |
|
50 |
+ "statuses_count": 2908, |
|
51 |
+ "favourites_count": 0, |
|
52 |
+ "created_at": "Fri Aug 28 00:00:00 +0800 2009", |
|
53 |
+ "following": false, |
|
54 |
+ "allow_all_act_msg": false, |
|
55 |
+ "remark": "", |
|
56 |
+ "geo_enabled": true, |
|
57 |
+ "verified": false, |
|
58 |
+ "allow_all_comment": true, |
|
59 |
+ "avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1", |
|
60 |
+ "verified_reason": "", |
|
61 |
+ "follow_me": false, |
|
62 |
+ "online_status": 0, |
|
63 |
+ "bi_followers_count": 215 |
|
64 | 64 |
} |
65 | 65 |
} |
66 | 66 |
MD |
@@ -22,10 +22,9 @@ |
||
22 | 22 |
|
23 | 23 |
<% if @agent.new_record? %> |
24 | 24 |
<div class="control-group type-select"> |
25 |
- <%= image_tag "spinner-arrows.gif", :class => "spinner" %> |
|
26 | 25 |
<%= f.label :type, :class => 'control-label' %> |
27 | 26 |
<div class="controls"> |
28 |
- <%= f.select :type, options_for_select(Agent.types.map {|type| [type.to_s.gsub(/^.*::/, ''), type.to_s] }, @agent.type), :class => 'span4' %> |
|
27 |
+ <%= f.select :type, options_for_select(Agent.types.map {|type| [type.to_s.gsub(/^.*::/, ''), type.to_s] }, @agent.type), {}, :class => 'span4 select2' %> |
|
29 | 28 |
</div> |
30 | 29 |
</div> |
31 | 30 |
<% end %> |
@@ -40,7 +39,7 @@ |
||
40 | 39 |
<div class="control-group"> |
41 | 40 |
<%= f.label :schedule, :class => 'control-label' %> |
42 | 41 |
<div class="controls schedule-region" data-can-be-scheduled="<%= @agent.can_be_scheduled? %>"> |
43 |
- <%= f.select :schedule, options_for_select(Agent::SCHEDULES.map {|s| [s.humanize.titleize, s] }, @agent.schedule), :class => 'span4' %> |
|
42 |
+ <%= f.select :schedule, options_for_select(Agent::SCHEDULES.map {|s| [s.humanize.titleize, s] }, @agent.schedule), {}, :class => 'span4' %> |
|
44 | 43 |
<span class='cannot-be-scheduled text-info'>This type of Agent cannot be scheduled.</span> |
45 | 44 |
</div> |
46 | 45 |
</div> |
@@ -51,7 +50,7 @@ |
||
51 | 50 |
<%= f.select(:source_ids, |
52 | 51 |
options_for_select((current_user.agents - [@agent]).map {|s| [s.name, s.id] }, |
53 | 52 |
@agent.source_ids), |
54 |
- {}, { :multiple => true, :size => 5, :class => 'span4 multi-select' }) %> |
|
53 |
+ {}, { :multiple => true, :size => 5, :class => 'span4 select2' }) %> |
|
55 | 54 |
<span class='cannot-receive-events text-info'>This type of Agent cannot receive events.</span> |
56 | 55 |
</div> |
57 | 56 |
</div> |
@@ -2,7 +2,10 @@ |
||
2 | 2 |
<div class='row'> |
3 | 3 |
<div class='span12'> |
4 | 4 |
<div class="page-header"> |
5 |
- <h2>Editing your <%= @agent.short_type %></h2> |
|
5 |
+ <h2> |
|
6 |
+ Editing your <%= @agent.short_type %> |
|
7 |
+ <%= image_tag "spinner-arrows.gif", :class => "spinner" %> |
|
8 |
+ </h2> |
|
6 | 9 |
</div> |
7 | 10 |
|
8 | 11 |
<%= render 'form' %> |
@@ -2,7 +2,10 @@ |
||
2 | 2 |
<div class='row'> |
3 | 3 |
<div class='span12'> |
4 | 4 |
<div class="page-header"> |
5 |
- <h2>Create a new Agent</h2> |
|
5 |
+ <h2> |
|
6 |
+ Create a new Agent |
|
7 |
+ <%= image_tag "spinner-arrows.gif", :class => "spinner" %> |
|
8 |
+ </h2> |
|
6 | 9 |
</div> |
7 | 10 |
|
8 | 11 |
<%= render 'form' %> |
@@ -90,12 +90,12 @@ |
||
90 | 90 |
|
91 | 91 |
<p> |
92 | 92 |
<b>Options:</b> |
93 |
- <pre><%= PP.pp(@agent.options, "") %></pre> |
|
93 |
+ <pre><%= JSON.pretty_generate @agent.options %></pre> |
|
94 | 94 |
</p> |
95 | 95 |
|
96 | 96 |
<p> |
97 | 97 |
<b>Memory:</b> |
98 |
- <pre><%= PP.pp(@agent.memory, "") %></pre> |
|
98 |
+ <pre><%= JSON.pretty_generate @agent.memory %></pre> |
|
99 | 99 |
</p> |
100 | 100 |
</div> |
101 | 101 |
</div> |
@@ -7,7 +7,7 @@ |
||
7 | 7 |
|
8 | 8 |
<p> |
9 | 9 |
<b>Payload:</b> |
10 |
- <pre><%= PP.pp(@event.payload, "") %></pre> |
|
10 |
+ <pre><%= JSON.pretty_generate @event.payload %></pre> |
|
11 | 11 |
</p> |
12 | 12 |
|
13 | 13 |
<% if @event.lat && @event.lng %> |
@@ -2,9 +2,23 @@ require 'jsonpath' |
||
2 | 2 |
require 'cgi' |
3 | 3 |
|
4 | 4 |
module Utils |
5 |
- # Unindents if the indentation is 2 or more characters. |
|
6 | 5 |
def self.unindent(s) |
7 |
- s.gsub(/^#{s.scan(/^\s+/).select {|i| i.length > 1 }.min_by{|l|l.length}}/, "") |
|
6 |
+ s = s.gsub(/\t/, ' ').chomp |
|
7 |
+ min = ((s.split("\n").find {|l| l !~ /^\s*$/ })[/^\s+/, 0] || "").length |
|
8 |
+ if min > 0 |
|
9 |
+ s.gsub(/^#{" " * min}/, "") |
|
10 |
+ else |
|
11 |
+ s |
|
12 |
+ end |
|
13 |
+ end |
|
14 |
+ |
|
15 |
+ def self.pretty_print(struct, indent = true) |
|
16 |
+ output = JSON.pretty_generate(struct) |
|
17 |
+ if indent |
|
18 |
+ output.gsub(/\n/i, "\n ").tap { |a| p a } |
|
19 |
+ else |
|
20 |
+ output |
|
21 |
+ end |
|
8 | 22 |
end |
9 | 23 |
|
10 | 24 |
def self.recursively_symbolize_keys(object) |
@@ -1,6 +1,32 @@ |
||
1 | 1 |
require 'spec_helper' |
2 | 2 |
|
3 | 3 |
describe Utils do |
4 |
+ describe "#unindent" do |
|
5 |
+ it "unindents to the level of the greatest consistant indention" do |
|
6 |
+ Utils.unindent(<<-MD).should == "Hello World" |
|
7 |
+ Hello World |
|
8 |
+ MD |
|
9 |
+ |
|
10 |
+ Utils.unindent(<<-MD).should == "Hello World\nThis is\nnot indented" |
|
11 |
+ Hello World |
|
12 |
+ This is |
|
13 |
+ not indented |
|
14 |
+ MD |
|
15 |
+ |
|
16 |
+ Utils.unindent(<<-MD).should == "Hello World\n This is\n indented\nthough" |
|
17 |
+ Hello World |
|
18 |
+ This is |
|
19 |
+ indented |
|
20 |
+ though |
|
21 |
+ MD |
|
22 |
+ |
|
23 |
+ Utils.unindent("Hello\n I am indented").should == "Hello\n I am indented" |
|
24 |
+ |
|
25 |
+ a = " Events will have the fields you specified. Your options look like:\n\n {\n \"url\": {\n \"css\": \"#comic img\",\n \"attr\": \"src\"\n },\n \"title\": {\n \"css\": \"#comic img\",\n \"attr\": \"title\"\n }\n }\"\n" |
|
26 |
+ Utils.unindent(a).should == "Events will have the fields you specified. Your options look like:\n\n {\n \"url\": {\n\"css\": \"#comic img\",\n\"attr\": \"src\"\n },\n \"title\": {\n\"css\": \"#comic img\",\n\"attr\": \"title\"\n }\n }\"" |
|
27 |
+ end |
|
28 |
+ end |
|
29 |
+ |
|
4 | 30 |
describe "#value_at" do |
5 | 31 |
it "returns the value at a JSON path" do |
6 | 32 |
Utils.value_at({ :foo => { :bar => :baz }}.to_json, "foo.bar").should == "baz" |
@@ -212,7 +212,7 @@ JSONEditor.prototype.showFunctionButtons = function(insider) { |
||
212 | 212 |
}).text('Redo')).append($('<a id="toggle_view" href="#" style="padding-right: 10px;"></a>').click(function() { |
213 | 213 |
self.toggleBuilder(); |
214 | 214 |
return false; |
215 |
- }).text('Toggle View').css("float", "right")); |
|
215 |
+ }).text('Toggle View')); |
|
216 | 216 |
this.container.prepend(this.functionButtons); |
217 | 217 |
this.container.height(this.container.height() + this.functionButtons.height() + 5); |
218 | 218 |
} |